package com.cn.onetrip.view;

import java.lang.annotation.Retention;

import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.ImageView;

public class MapImageView extends ImageView {

	private Context context;
	private float x_down = 0;
	private float y_down = 0;
	private PointF mid = new PointF();
	private float oldDist = 1f;
	private float oldRotation = 0;
	private Matrix matrix = new Matrix();
	private Matrix matrixTemp = new Matrix();
	private Matrix savedMatrix = new Matrix();

	private static final int NONE = 0;
	private static final int DRAG = 1;
	private static final int ZOOM = 2;

	private final float MAX_SCALE = 2;

	private int mode = NONE;

	private final int OFFSET = 60;

	private boolean matrixCheck = false;

	private int widthScreen;
	private int heightScreen;
	private int minWidth;
	private int minHeight;
	private int minScale = 4;

	private Bitmap gintama = null;
	private float scale = 1;

	public final int MPERSP_0 = 6;
	public final int MPERSP_1 = 7;
	public final int MPERSP_2 = 8;
	public final int MSCALE_X = 0;
	public final int MSCALE_Y = 4;
	public final int MSKEW_X = 1;
	public final int MSKEW_Y = 3;
	public final int MTRANS_X = 2;
	public final int MTRANS_Y = 5;

	private float[] matrixValues = new float[9];

	private Handler msgHandler = null;

	public MapImageView(Context context) {
		super(context);
		this.context = context;
	}

	public MapImageView(Context context, AttributeSet attr) {
		super(context, attr);
		this.context = context;
	}

	public void InitMapView(Bitmap bmpMap, Handler handler, boolean isInMapPage) {
		gintama = bmpMap;

		msgHandler = handler;
  
		widthScreen = this.getWidth();
		heightScreen = this.getHeight();

		minHeight = heightScreen / minScale;
		minWidth = (int) (((float) bmpMap.getWidth() / bmpMap.getHeight()) * minHeight);

		matrix = new Matrix();

		scale = (float) widthScreen / gintama.getWidth();
		if ((isInMapPage && scale > (float) heightScreen / gintama.getHeight()) || 
			(!isInMapPage && scale < (float) heightScreen / gintama.getHeight()))
			scale = (float) heightScreen / gintama.getHeight();

		matrix.postScale(scale, scale);
		matrix.postTranslate(-(gintama.getWidth() * scale - widthScreen) / 2,
				-(gintama.getHeight() * scale - heightScreen) / 2);

		invalidate();

//
//		int firstPtX = (int) (ptFirst.x * scale);
//		int firstPtY = (int) (ptFirst.y * scale);
//
//		int boundTop = (int) ((gintama.getHeight() * scale - heightScreen) / 2);
//		int boundLeft = (int) ((gintama.getWidth() * scale - widthScreen) / 2);
//		int boundBottom = (int) ((gintama.getHeight() * scale - heightScreen) / 2)
//				+ heightScreen;
//		int boundRight = (int) ((gintama.getWidth() * scale - widthScreen) / 2)
//				+ widthScreen;
//
//		Rect rect = new Rect(boundLeft, boundTop, boundRight, boundBottom);
//
//		if (rect.contains(firstPtX, firstPtY)) {
//			matrix.postTranslate(
//					-(gintama.getWidth() * scale - widthScreen) / 2,
//					-(gintama.getHeight() * scale - heightScreen) / 2);
//		} else {
//
//			if (firstPtX > boundRight)
//				boundLeft += (firstPtX - boundRight) + OFFSET;
//			else if (firstPtX < boundLeft)
//				boundLeft -= (boundLeft - firstPtX) - OFFSET;
//
//			if (firstPtY > boundBottom)
//				boundTop += (firstPtY - boundBottom) + OFFSET;
//			else if (firstPtY > boundBottom)
//				boundTop -= (boundTop - firstPtY) - OFFSET;
//
//			matrix.postTranslate(-boundLeft, -boundTop);
//		}
//
//		invalidate();
	}
	
	public void configurationChanged() {
		int temp = widthScreen;
		widthScreen = heightScreen;
		heightScreen = temp;
		
		temp = minWidth;
		minWidth = minHeight;
		minHeight = temp;
		
//		resetMapPos();
	}
	
	public void setBitmap(Bitmap bmp) {
		gintama = bmp;
	}

	public Point getXYPoint() {
		Point pt = new Point();

		matrix.getValues(matrixValues);

		pt.x = (int) matrixValues[MTRANS_X];
		pt.y = (int) matrixValues[MTRANS_Y];

		return pt;
	}

	public float getMapWidth() {
		matrix.getValues(matrixValues);

		return (gintama.getWidth() * matrixValues[MSCALE_X]);
	}

	public float getMapHeight() {
		matrix.getValues(matrixValues);

		return (gintama.getHeight() * matrixValues[MSCALE_Y]);
	}

	protected void onDraw(Canvas canvas) {
		canvas.save();
		if (gintama != null)
			canvas.drawBitmap(gintama, matrix, null);
		canvas.restore();
	}

	public boolean onTouchEvent(MotionEvent event) {
		if (gintama == null)
			return true;

		switch (event.getAction() & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:
			mode = DRAG;
			x_down = event.getX();
			y_down = event.getY();
			savedMatrix.set(matrix);
			break;
		case MotionEvent.ACTION_POINTER_DOWN:
			mode = ZOOM;
			oldDist = spacing(event);
			oldRotation = rotation(event);
			savedMatrix.set(matrix);
			midPoint(mid, event);
			break;
		case MotionEvent.ACTION_MOVE:
			if (mode == ZOOM) {
				if (scale > MAX_SCALE)
					return true;

				matrixTemp.set(savedMatrix);
				float newDist = spacing(event);
				float scaleTemp = newDist / oldDist;
				matrixTemp.postScale(scaleTemp, scaleTemp, mid.x, mid.y);
				matrixCheck = matrixCheck();
				if (matrixCheck == false) {
					matrix.set(matrixTemp);
					invalidate();

					Message msg = new Message();
					msg.arg1 = 0;
					msgHandler.sendMessage(msg);
				}
			} else if (mode == DRAG) {
				float offsetX = event.getX() - x_down;
				float offsetY = event.getY() - y_down;
				
				matrixTemp.set(savedMatrix);

				matrixTemp.postTranslate(offsetX, offsetY);
				matrix.set(matrixTemp);
				matrixCheck = matrixCheck();

				invalidate();

				Message msg = new Message();
				msg.arg1 = 0;
				msgHandler.sendMessage(msg);
			}
			break;
		case MotionEvent.ACTION_UP:
		case MotionEvent.ACTION_POINTER_UP:
			mode = NONE;
			break;
		}
		return true;
	}
	
	private void resetMapPos() {
		
		matrix.postTranslate(0, 0);
		matrixCheck = matrixCheck();

		invalidate();
		
		Message msg = new Message();
		msg.arg1 = 0;
		msgHandler.sendMessage(msg);
	}

	private boolean matrixCheck() {
		float[] f = new float[9];
		matrixTemp.getValues(f);
		// 4 point condition
		float x1 = f[0] * 0 + f[1] * 0 + f[2];
		float y1 = f[3] * 0 + f[4] * 0 + f[5];
		float x2 = f[0] * gintama.getWidth() + f[1] * 0 + f[2];
		float y2 = f[3] * gintama.getWidth() + f[4] * 0 + f[5];
		float x3 = f[0] * 0 + f[1] * gintama.getHeight() + f[2];
		float y3 = f[3] * 0 + f[4] * gintama.getHeight() + f[5];
		float x4 = f[0] * gintama.getWidth() + f[1] * gintama.getHeight()
				+ f[2];
		float y4 = f[3] * gintama.getWidth() + f[4] * gintama.getHeight()
				+ f[5];

		float width = (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2)
				* (y1 - y2));
		float height = (float) Math.sqrt((x2 - x4) * (x2 - x4) + (y2 - y4)
				* (y2 - y4));

		// Log.i("Scale", ""+width/gintama.getWidth());
		Log.i("Point", "" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + x3
				+ "," + y3 + "," + x4 + "," + y4);

		if ((Math.round(width) < minWidth || Math.round(height) < minHeight || Math
				.round(width) > (double) gintama.getWidth() * MAX_SCALE)
				&& scale < MAX_SCALE) {
			return true;
		}
		
		if ((Math.round(width) >= widthScreen && x1 >= 0) 
				|| (Math.round(width) < widthScreen && x1 <= 0)) {
			matrixTemp.postTranslate(0 - x1, 0);
			matrix.set(matrixTemp);
			invalidate();
		}

		if ((Math.round(height) >= heightScreen && y1 >= 0) 
				|| (Math.round(height) < heightScreen && y1 <= 0)) {
			matrixTemp.postTranslate(0, 0 - y1);
			matrix.set(matrixTemp);
			invalidate();
		}

		if ((Math.round(width) >= widthScreen && x2 <= widthScreen) 
				|| (Math.round(width) < widthScreen && x2 >= widthScreen)) {
			matrixTemp.postTranslate(widthScreen - x2, 0);
			matrix.set(matrixTemp);
			invalidate();
		}

		if ((Math.round(height) >= heightScreen && y3 <= heightScreen) 
				|| (Math.round(height) < heightScreen && y3 >= heightScreen)) {
			matrixTemp.postTranslate(0, heightScreen - y3);
			matrix.set(matrixTemp);
			invalidate();
		}

		return false;
	}

	private float spacing(MotionEvent event) {
		float x = event.getX(0) - event.getX(1);
		float y = event.getY(0) - event.getY(1);
		return FloatMath.sqrt(x * x + y * y);
	}

	private void midPoint(PointF point, MotionEvent event) {
		float x = event.getX(0) + event.getX(1);
		float y = event.getY(0) + event.getY(1);
		point.set(x / 2, y / 2);
	}

	private float rotation(MotionEvent event) {
		double delta_x = (event.getX(0) - event.getX(1));
		double delta_y = (event.getY(0) - event.getY(1));
		double radians = Math.atan2(delta_y, delta_x);
		return (float) Math.toDegrees(radians);
	}
}
